package com.henz4help.app.channel;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import com.henz4help.app.common.HttpWorker;
import com.henz4help.app.common.Util;

public class GaeChannel {
	
	private HttpWorker mHttpWorker;

    public interface ChannelListener {

    	public void onOpen();

    	public void onMessage(String message);

    	public void onError(int code, String description);

    	public void onClose();

    }
    private enum ReadyState {
        CONNECTING,
        OPEN,
        CLOSING,
        CLOSED
    };

    private class XHR {

        private String mResponseText;

        private int mStatus;

        private String mStatusText;

        public XHR(HttpResponse response) throws IOException {
            StatusLine statusLine = response.getStatusLine();
            mStatus = statusLine.getStatusCode();
            mStatusText = statusLine.getReasonPhrase();
            HttpEntity httpEntity = response.getEntity();
            mResponseText = IOUtils.toString(httpEntity.getContent(), "UTF-8");
            //log.trace("RECV << " + StringUtils.chomp(mResponseText));
        }

        public String getStatusText() {
            return mStatusText;
        }

        public int getStatus() {
            return mStatus;
        }

        public boolean isSuccess() {
            return (mStatus == HttpStatus.SC_OK);
        }

        public String getResponseText() {
            return mResponseText;
        }

        public String toString() {
            return "xhr[status="+mStatus+",error="+mStatusText+",message="+mResponseText+"]";
        }
    }

    private ChannelListener defaultChannelListener = new ChannelListener() {

        @Override
        public void onOpen() {
        	Util.Log("unhandled: onOpen");
        }

        @Override
        public void onMessage(String message) {
        	Util.Log("unhandled: onMessage = " + message);
        }

        @Override
        public void onError(int code, String description) {
        	Util.Log("unhandled: onError = " + description + " [" + code + "]");
        }

        @Override
        public void onClose() {
        	Util.Log("unhandled: onClose");
        }

    };

    //private static final String BASE_URL = "http://localhost:8888";
    private static final String BASE_URL = "http://1-dot-henzmobile.appspot.com";
    

    private String mChannelId = null;


    private String mClientId = null;

    private ChannelListener mChannelListener = defaultChannelListener;

    private ReadyState mReadyState = ReadyState.CLOSED;

    private int POLLING_TIMEOUT_MS = 500;

    private HttpClient httpClient = new DefaultHttpClient();

    private Thread thPoll = null;

    public GaeChannel(String channelId, ChannelListener channelListener) {
    	mHttpWorker = new HttpWorker();
    	mChannelId = channelId;
        mClientId = null;
        if (channelListener != null) {
            mChannelListener = channelListener;
        }
    }

    public void open() throws Exception {
        mReadyState = ReadyState.CONNECTING;
        String url = BASE_URL + "/channel/token.json";
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("channelId", mChannelId);
		connect(
        	new XHR( mHttpWorker.getJsonResponse( url, params  ) )
        );
        
    }

    public void close() throws IOException {
        mReadyState = ReadyState.CLOSING;
        //disconnect(sendGet(getUrl("disconnect")));
    }

    private void connect(XHR xhr) {
        if (xhr.isSuccess()) {
            mClientId = xhr.getResponseText();
            mReadyState = ReadyState.OPEN;
            mChannelListener.onOpen();
            poll();
        } else {
            mReadyState = ReadyState.CLOSING;
            mChannelListener.onError(xhr.getStatus(), xhr.getStatusText());
            mReadyState = ReadyState.CLOSED;
            mChannelListener.onClose();
        }
    }

    /*
    private void disconnect(XHR xhr) {
        mReadyState = ReadyState.CLOSED;
        mChannelListener.onClose();
    }
    */

    private void forwardMessage(XHR xhr) {
        if (xhr.isSuccess()) {
            String data = StringUtils.chomp(xhr.getResponseText());
            if (!StringUtils.isEmpty(data)) {
                mChannelListener.onMessage(data);
            }
            poll();
        } else {
            mChannelListener.onError(xhr.getStatus(), xhr.getStatusText());
        }
    }
    
    private String getUrl(String command) throws IOException {
    	String url = BASE_URL + "/_ah/channel/dev?command=" + command + "&channel=";
        url += URLEncoder.encode(mChannelId, "UTF-8");
        if (mClientId != null) {
            url += "&client=" + URLEncoder.encode(mClientId, "UTF-8");
        }
        return url;
    };

    private void poll() {
        if (thPoll == null) {
            thPoll = new Thread(new Runnable() {

                @Override
                public void run() {
                    XHR xhr = null;
                    try {
                        Thread.sleep(POLLING_TIMEOUT_MS);
                        //long tm = System.currentTimeMillis();
                        xhr = sendGet(getUrl("poll"));
                        thPoll = null;
                        forwardMessage(xhr);
                    } catch (Exception e) {
                    	Util.Log("Poll failed");
                        thPoll = null;
                    }
                }

            });
            thPoll.start();
        } else {
            Util.Log("Can't poll - still polling");
        }
    }

    public void beforeUnload() throws IOException {
        //sendGet(getUrl("disconnect"));
    }

    private void forwardSendComplete(XHR xhr) {
        if (!xhr.isSuccess()) {
            mChannelListener.onError(xhr.getStatus(), xhr.getStatusText());
        }
    }
    public boolean send(String msg) throws Exception {
        if (mReadyState != ReadyState.OPEN) {
            return false;
        }
        
        String url = BASE_URL + "/channel/sendChannel.json";
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("channelId", mChannelId);
        params.put("message", msg);
        forwardSendComplete(
        	new XHR( mHttpWorker.getJsonResponse( url, params  ) )
        );
        
        
        return true;
    }
    
    /*
    private XHR sendPost(String url, List<NameValuePair> params) throws IOException {
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "UTF-8");
        HttpPost httpPost = new HttpPost(url);
        
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Content-Type", "application/json");
        
        httpPost.setEntity(entity);
        Util.Log("POST >> " + url );
        Util.Log("POST >> " + params );
        Util.Log("POST >> " + StringUtils.chomp(entity.toString()));
        return new XHR(httpClient.execute(httpPost));
    }
    */
    
    private XHR sendGet(String url) throws IOException {
        HttpGet httpGet = new HttpGet(url);
        Util.Log("GET  >> " + StringUtils.chomp(httpGet.getURI().getQuery()));
        return new XHR(httpClient.execute(httpGet));
    }


}
